#include "recommend.h"

int compare(void const *a, void const *b) {
	//TRIE EN DECROISSANT
	double temp;
	temp	=	*(double*)a - *(double*)b ;
	
	
	/*
	 * const double *da = (const double *) a;
	 * const double *db = (const double *) b;
	 *
	 * return (*da > *db) - (*da < *db);
	 */
	
	if(temp<0){
		return(1);
	}
	if(temp>0){
		return(-1);
	}
	return (0);
}
/*
 * int compare(void const *a, void const *b) {
 * //TRIE EN DECROISSANT
 * double temp;
 * temp	=	*(double*)a - *(double*)b ;
 * if(temp<0){
 * return(*(double*)a);
 * }
 * if(temp>0){
 * return(*(double*)b);
 * }
 * return (0);
 * }
 **/


recommend::recommend(int n_users, int n_movies){
	thistest_set		=	Matrix(n_users, n_movies);
	thistraining_set	=	Matrix(n_users, n_movies);
	
	thisNusers		=	n_users;
	thisNmovies		=	n_movies;
	
	thissuggestions_val	=	Matrix(n_users, n_movies);
	
	cout << "==================================" << endl;
	cout << "     RECOMMENDATION TOOLS" << endl;
	cout << "==================================" << endl;
	cout << "Benoit Vermoortel, Mech.E." << endl;
	cout << "Benoit.Vermoortel@gmail.com" << endl;
	cout << "Thesis at FUCaM, 2008" << endl;
	cout << "http://code.google.com/p/recommendtools/" << endl;
	cout << "==================================" << endl;

}

int recommend::LoadTest_set(string fname){
	
	int temp_user, temp_movie, temp_rating;
	
	
	thistest_set.fill(0);
	
	ifstream fin(fname.data());
	
	if(fin.fail()){
		cout << fname.data() << " not found!" << endl;
		return(-1);
	}
	else{
		cout << "Loading :" << fname.data() << endl;
		while(fin.eof()==false){
			fin >> temp_user;
			fin >> temp_movie;
			//fin >> temp_rating;
			//cout << temp_user << "\t" << temp_movie << endl;
			thistest_set(temp_user-1, temp_movie-1)=1;
		}
		return(0);
	}
	//cout << thistest_set;
	//cout << (thistest_set.sum(1)).sum(2) << endl;
}

int recommend::LoadTest_set_3lines(string fname){

	int temp_user, temp_movie, temp_rating;


	thistest_set.fill(0);

	ifstream fin(fname.data());

	if(fin.fail()){
		cout << fname.data() << " not found!" << endl;
		return(-1);
	}
	else{
		cout << "Loading :" << fname.data() << endl;
		while(fin.eof()==false){
			fin >> temp_user;
			fin >> temp_movie;
			fin >> temp_rating;
			//cout << temp_user << "\t" << temp_movie << endl;
			thistest_set(temp_user-1, temp_movie-1)=1;
		}
		return(0);
	}
	//cout << thistest_set;
	//cout << (thistest_set.sum(1)).sum(2) << endl;
}
int recommend::LoadTest_set_3lines_R(string fname){

	int temp_user, temp_movie, temp_rating;


	thistest_set.fill(0);

	ifstream fin(fname.data());

	if(fin.fail()){
		cout << fname.data() << " not found!" << endl;
		return(-1);
	}
	else{
		cout << "Loading :" << fname.data() << endl;
		while(fin.eof()==false){
			fin >> temp_user;
			fin >> temp_movie;
			fin >> temp_rating;
			//cout << temp_user << "\t" << temp_movie << endl;
			thistest_set(temp_user-1, temp_movie-1)=temp_rating;
		}
		return(0);
	}
	//cout << thistest_set;
	//cout << (thistest_set.sum(1)).sum(2) << endl;
}

int recommend::LoadTraining_set_R(string fname){

	int temp_user, temp_movie, temp_rating;

	thistraining_set.fill(0);

	ifstream fin(fname.data());

	if(fin.fail()){
		cout << fname.data() << " not found!" << endl;
		return(-1);
	}
	else{
		cout << "Loading :" << fname.data() << endl;
		while(fin.eof()==false){
			fin >> temp_user;
			fin >> temp_movie;
			fin >> temp_rating;
			//cout << temp_user << "\t" << temp_movie << endl;
			thistraining_set(temp_user-1, temp_movie-1)	=	temp_rating;
		}
		//cout << thistraining_set << endl;
		return(0);

	}
	//cout << thistest_set;
	//cout << (thistest_set.sum(1)).sum(2) << endl;
}

int recommend::LoadTraining_set(string fname){
	
	int temp_user, temp_movie, temp_rating;
	
	thistraining_set.fill(0);
	
	ifstream fin(fname.data());
	
	if(fin.fail()){
		cout << fname.data() << " not found!" << endl;
		return(-1);
	}
	else{
		cout << "Loading :" << fname.data() << endl;
		while(fin.eof()==false){
			fin >> temp_user;
			fin >> temp_movie;
			fin >> temp_rating;
			//cout << temp_user << "\t" << temp_movie << endl;
			thistraining_set(temp_user-1, temp_movie-1)	=	1;
		}
		//cout << thistraining_set << endl;
		return(0);
		
	}
	//cout << thistest_set;
	//cout << (thistest_set.sum(1)).sum(2) << endl;
}

int recommend::SuggestMeSomethingMaxF(){
	Matrix temp=Matrix(thisNusers, 1);
	
	//cout << (thistraining_set.sum(2)).length() << endl;
	//cout << thistraining_set.sum(2) << endl;
	temp.fill(1);
	thissuggestions_val	= temp*thistraining_set.sum(2);
	//cout << thissuggestions_val << endl;
	ofstream fout1("output/maxf.dat");
	fout1 << thissuggestions_val << endl;
	fout1.close();


        ofstream fout2("output/training.dat");
	fout2 << thistraining_set << endl;
	fout2.close();
	
        ofstream fout3("output/test.dat");
	fout3 << thistest_set << endl;
	fout3.close();

        return(0);
}

int recommend::SuggestMeSomethingLp(){
	Matrix Lp=Matrix(thisNusers+thisNmovies, thisNusers+thisNmovies);
	
	Lp	=	ComputeLp();
	
	Lp.resize(thisNusers, thisNusers);
	/*
	 * ofstream fout("Lplus_output.dat");
	 * fout << Lp << endl;
	 * fout.close();
	 */
	//Lp	=	ZerosOnDiag(Lp);
	
	thissuggestions_val	=	Lp*thistraining_set;
	/*
	 * ofstream fout1("Suggestions_output.dat");
	 * fout1 << thissuggestions_val << endl;
	 * fout1.close();
	 */
	return(0);
	
	//UserBased(Lp,20);
}




double recommend::AnalysisDegreeOfAgreement(int index_user){
	Matrix M1;
	Matrix M2;
	int iu, im;
	int k, l;
	int counter=0;
	int n_pairs;
	int M1_k;
	int M2_l;
	double dtemp1, dtemp2;
	
	
	iu=index_user;
	
	
	for(im=0;im<thisNmovies;im++){
		if(thistest_set(iu, im)>0){//quand le film est dans le test set
			//cout << "resize" << endl;
			M1.resize(1, M1.length()+1);
			//cout << M1.rows() << M1.cols() << endl;
			M1(0, M1.length()-1)	=	im;
		}
		else{
			if(thistraining_set(iu, im)>0){//quand le film est dans le training set
				
			}
			else{//ni dans le training, ni dans le test
				M2.resize(1, M2.length()+1);
				M2(0, M2.length()-1)	=	im;
			}
		}
	}
	//cout << "M1:" << M1.length() << endl;
	//cout << "M2:" << M2.length() << endl;
	//cout << "M1:" << M1 << endl;
	//cout << "M2:" << M2 << endl;
	if(M1.length()==0 || M2.length()==0){
		return(-1);
	}
	for(k=0;k<M1.length();k++){
		M1_k	=	(int)M1(k);
		
		for(l=0;l<M2.length();l++){
			M2_l	=	(int)M2(l);
			//cout << "M2_l: " << M2_l << "  M1_l: " << M1_k << endl;
			//cout << "predM1: " << thissuggestions_val(M1_k) << " predM2: " << thissuggestions_val(M2_l) << endl;
			if(thissuggestions_val(iu, M1_k)>=thissuggestions_val(iu, M2_l)){
				dtemp1	=	thissuggestions_val(M1_k);
				dtemp2	=	thissuggestions_val(M2_l);
				// la valeur de l'element du test set est plus grand que celle de M2
				counter++;
				//cout << counter << endl;
			}
		}
	}
	//cout << n << "," << (int)M2.length()+M1.length() << endl;
	n_pairs	=	(int)M1.length()*M2.length();
	//cout << M1 << endl;
	//cout << M2.length() << endl;
//	if(n_pairs==counter){
//		counter=-counter;
//	}
	//cout << "Paires:" << n_pairs << endl;
	return((double)counter/(double)n_pairs);
}

double recommend::AnalysisPercentile(int index_user){
	Matrix test_set_positions;
	int iu, i, j, n;
	Array<double> t;
	Array<double> t2;
	RowVector vsuggestion;
	Matrix short_test_set;
	Matrix short_test_set_position;
	double temp;
	
	iu=index_user;
	
	vsuggestion=thissuggestions_val.row(iu);
	t= vsuggestion.qsort(compare);
	
	
	for(i=0;i<thisNmovies;i++){
		if(thistest_set(iu, i)>0){
			short_test_set.resize(1, short_test_set.length()+1);
			short_test_set(0, short_test_set.length()-1)	=	thissuggestions_val(iu, i);
		}
	}
	if(short_test_set.length()==0){
		return(-1);
	}
	short_test_set_position.resize(1, (int)short_test_set.length());
	
	//cout << short_test_set << endl;
	for(i=0;i<thisNmovies;i++){
		for(j=0;j<short_test_set_position.length();j++){
			if(t(i)==short_test_set(0, j)){
				short_test_set_position(0, j)=i+1;
			}
		}
	}
	t2	=	short_test_set_position.qsort(compare);
	//cout << "stp:" << short_test_set_position << endl;
	
	n	=	(int)short_test_set_position.length();
	
	
	if(floor(n/2)==(double)n/2){
		temp=(double)(t2(n/2)+t2(n/2-1))/(2*vsuggestion.length());
		//cout << n << "pair" << endl;
	}
	else{
		temp=(double)(t2((n-1)/2))/(vsuggestion.length());
		//cout << n << "impair " << endl;
	}
	
	return(temp);
	
}

double recommend::AnalysisRecall(int index_user, int n){
	Array<double> t;
	RowVector vsuggestion;
	double Min_val;
	int i=0, iu, counter=0, counter_nonnul=0;
	
	iu=index_user;
	
	RemoveTrainingFromPredictions(iu, -1);//remplace par -1 ceux qui etaient dans le training set
	vsuggestion=thissuggestions_val.row(iu);
	
	
	t= vsuggestion.qsort(compare);//trie les suggestion
	Min_val	=	t(n-1);

        ofstream fout("output/brol.dat");
        for(i=0;i<t.length();i++){
            fout << t(i) << endl;
        }
        fout << endl << endl;
        fout << thissuggestions_val.row(iu);

        fout.close();



	if(Min_val<0){//retourne 0 si training set trop grand
		return(-1);
	}
	
	
	
	for(i=0;i<thisNmovies;i++){
		if(thistest_set(iu, i)>0){
			counter_nonnul++;//compte les valeurs non nulles du testset
			if(thissuggestions_val(iu, i)>=Min_val){
				counter++;//compte les valeurs >= a la valeur min
                                //bref: counter=nbr de truc du test set qui sont >Min_val dans le classement
			}
		}
	}
	if(counter_nonnul>0){
            //parmis les nbr du test set, combien sont dans les 10premiers
		return((double)counter/counter_nonnul);
	}
	else{
		return(-1);
	}
	
}

int recommend::UserBased(Matrix sim, int k){
	int iu, im, i;
	double tot;
	RowVector simv;
	Array<double> t;
	double MinVal;
	Matrix simu=Matrix(sim.rows(), sim.cols());
	RowVector temp;
	
	
	temp.fill(1);
	
	simu	=	sim;
	simu.resize(thisNusers, thisNusers);
	
	for(iu=0;iu<thisNusers;iu++){
		simv	=	simu.row(iu);
		t	=	simv.qsort(compare);
		simv	=	simu.row(iu);
		/*
		 * for(i=0;i<k-1;i++){
		 * cout << t(i) << endl;
		 * }
		 */
		MinVal	=	t(k-1);
		//cout << MinVal << endl;
		tot	=	0;
		for(i=0;i<thisNusers;i++){
			if(simv(i)<MinVal){
				simv(i)	=	0;
			}
			else{
				tot+=simv(i);
			}
			//cout << simv(i) << " ; ";
		}
		if(tot==0){
			return(-1);
		}
		
		//a	=	thistraining_set.row(iu);
		
		temp	=	simv*thistraining_set;
		for(i=0;i<thisNmovies;i++){
			thissuggestions_val(iu, i)=temp(i)/tot;
		}
		
	}
	cout << "Fichier suggestions..." << endl;
	ofstream fout("sug_user_based.dat");
	fout << thissuggestions_val << endl;
	fout.close();
	
	//RemoveTrainingFromPredictions(0);
	
	return(0);
}


int recommend::MovieBased(Matrix sim, int k){
	int iu, im, i;
	double tot;
	ColumnVector simv;
	Array<double> t;
	double MinVal;
	Matrix simu=Matrix(thisNmovies, thisNmovies);
	RowVector temp;
	
	
	temp.fill(1);
	
	simu	=	sim;
	simu.resize(thisNmovies, thisNmovies);
	
	for(im=0;im<thisNmovies;im++){
		simv	=	simu.column(im);
		t	=	simv.qsort(compare);
		simv	=	simu.column(im);
		/*
		 * for(i=0;i<k-1;i++){
		 * cout << t(i) << endl;
		 * }
		 */
		MinVal	=	t(k-1);
		//cout << MinVal << endl;
		tot	=	0;
		for(i=0;i<thisNmovies;i++){
			if(simv(i)<MinVal){
				simv(i)	=	0;
			}
			else{
				tot+=simv(i);
			}
		}
		if(tot==0){
			return(-1);
		}
		
		//a	=	thistraining_set.row(iu);
		
		temp	=	thistraining_set*simv;
		for(i=0;i<thisNusers;i++){
			thissuggestions_val(i, im)=temp(i)/tot;
		}
		
	}
	cout << "Fichier suggestions..." << endl;
	ofstream fout("sug_movie_based.dat");
	fout << thissuggestions_val << endl;
	fout.close();
	
	RemoveTrainingFromPredictions(0);
	
	return(0);
}

Matrix recommend::ComputeLp(){
	Matrix A=Matrix(thisNusers+thisNmovies, thisNusers+thisNmovies);
	Matrix temp=Matrix(thisNusers+thisNmovies, 1);
	Matrix D;
	Matrix conv;
	int iu, im, j, i;
	
	Matrix A_red;
	Matrix Lp_red;
	Matrix L_red;
	Matrix tempones;
	Matrix Lp=Matrix(thisNusers+thisNmovies, thisNusers+thisNmovies);
	cout << "Matrice d'adjacence" << endl;
	A.fill(0);
	for(iu=0;iu<thisNusers;iu++){
		for(im=0;im<thisNmovies;im++){
			if(thistraining_set(iu, im)>0){
				A(iu, thisNusers+im)	=	thistraining_set(iu, im);
				A(thisNusers+im, iu)	=	thistraining_set(iu, im);
			}
		}
	}
	temp=A.sum(2);
	j=0;
	
	conv	=	Matrix(temp.length(), temp.length());
	conv.fill(0);
	D	=	Matrix(temp.length(), temp.length());
	D.fill(0);
	
	cout << "Conv" << endl;
	for(i=0;i<temp.length();i++){
		if(temp(0, i)==0){
			
		}
		else{
			//conv.resize(i+1,j+1);
			D(j, j)		=	temp(0, i);
			conv(i, j)	=	1;
			j++;
		}
	}
	conv.resize(i, j);
	D.resize(j, j);
	//cout << j << endl;
	//cout << conv << endl;
	A_red	=	Matrix(j, j);
	A_red	=	conv.transpose()*A*conv;
	
	tempones	=	Matrix(j, j);
	tempones.fill(1);
	
	L_red	=	D-A_red;
	
	
	Lp_red	=	Matrix(j, j);
	//Lp_red	=	L_red.pseudo_inverse();
	Lp_red	=	(L_red-tempones/j).inverse()+tempones/j;
	Lp	=	conv*Lp_red*conv.transpose()*1000;
	
	//cout << Lp.length() << endl;
	
	ofstream fout("Lp.dat");
	fout << Lp << endl;
	fout.close();
	
	return(Lp);
}

int recommend::SuggestMeSomethingLpUserBased(int k){
	Matrix Lp=Matrix(thisNusers+thisNmovies, thisNusers+thisNmovies);
	int i;
	
	
	//===========================================
	/*
	 * Matrix test=Matrix(5,1);
	 *
	 * test(0,0)	=	.1;
	 * test(1,0)	=	.4;
	 * test(2,0)	=	.9;
	 * test(3,0)	=	.2;
	 * test(4,0)	=	.0;
	 *
	 * test.qsort(compare);
	 * cout << test << endl;
	 */
	
	//===========================================
	
	Lp	=	ComputeLp();
	//Lp.resize(thisNusers,thisNusers);
	
	Lp	=	ZerosOnDiag(Lp);
	
	UserBased(Lp, k);
}

int recommend::SuggestMeSomethingLpMovieBased(int k){
	Matrix Lp=Matrix(thisNusers+thisNmovies, thisNusers+thisNmovies);
	int i, j;
	Matrix temp=Matrix(thisNmovies, thisNmovies);
	
	Lp	=	ComputeLp();
	
	for(i=thisNusers;i<Lp.rows();i++){
		for(j=thisNusers;j<Lp.cols();j++){
			temp(i-thisNusers, j-thisNusers)=Lp(i, j);
		}
		
	}
	
	temp	=	ZerosOnDiag(temp);
	
	//Lp.resize(thisNusers,thisNusers);
	
	
	//Lp	=	ZerosOnDiag(Lp);
	
	MovieBased(temp, k);
	return(0);
}

int recommend::RemoveTrainingFromPredictions(int IndexUser, int replace){
	int iu;
	int i;
	
	iu	=	IndexUser;
	
	for(i=0;i<thisNmovies;i++){
		if(thistraining_set(iu, i)>0){
			thissuggestions_val(iu, i)	=	replace;
		}
	}
	return(0);
}
int recommend::RemoveTrainingFromPredictions(int replace){
	int i;
	
	for(i=0;i<thisNusers;i++){
		RemoveTrainingFromPredictions(i, replace);
	}
	return(0);
}

Matrix recommend::ZerosOnDiag(Matrix A){
	int i;
	
	for(i=0;i<A.cols();i++){
		A(i, i)	=	0;
	}
	return(A);
}


int recommend::SuggestMeSomethingWWtUserBased(int k){
	Matrix TEMP=Matrix(thisNusers, thisNusers);
	int i;
	
	TEMP	=	thistraining_set*thistraining_set.transpose();
	
	TEMP	=	ZerosOnDiag(TEMP);
	
	UserBased(TEMP, k);
	return(0);
}


int recommend::SuggestMeSomethingWWtMovieBased(int k){
	Matrix TEMP=Matrix(thisNusers, thisNusers);
	int i;
	
	TEMP	=	thistraining_set.transpose()*thistraining_set;
	
	TEMP	=	ZerosOnDiag(TEMP);
	
	MovieBased(TEMP, k);
	return(0);
}


int recommend::SuggestMeSomethingBinaryUserBased(int k){
	Matrix TEMP	=Matrix(thisNusers, thisNusers);
	RowVector tempv1	=RowVector(thisNmovies);
	RowVector tempv2	=RowVector(thisNmovies);
	RowVector temp		=RowVector(thisNmovies);
	
	temp.fill(1);
	
	double a, b, c, d, p;
	
	int iu, iu1;
	
	//TEMP	=	thistraining_set*thistraining_set.transpose();
	
	
	for(iu=0;iu<thisNusers;iu++){
		for(iu1=iu;iu1<thisNusers;iu1++){
			tempv1	=	thistraining_set.row(iu);
			tempv2	=	thistraining_set.row(iu1);
			//cout << "a" << endl;
			a	=	tempv1*tempv2.transpose();
			//cout << "b" << endl;
			b	=	tempv1*temp.transpose()-a;
			//cout << "c" << endl;
			c	=	tempv2*temp.transpose()-a;
			//cout << "d" << endl;
			d	=	tempv1.length()-a-b-c;
			//cout << "p" << endl;
			p	=	a+b+c+d;
			//cout << "TEMP" << endl;
			TEMP(iu, iu1)	=	(a+d)/p;
			TEMP(iu1, iu)	=	TEMP(iu, iu1);
		}
	}
	
	
	UserBased(TEMP, k);
	return(0);
}



int recommend::SuggestMeSomethingDWalk(int mode,int k,int inL){
	
	MyGraph TheGraph(thisNusers+thisNmovies);
	int i, j, L, line, iu,il;
	ostringstream tempstr;
	Matrix P=Matrix(thisNusers+thisNmovies, thisNusers+thisNmovies);
	ColumnVector cvtemp;
	//double temp;
	Matrix temp1(thisNusers+thisNmovies, thisNusers+thisNmovies);
	//Matrix Alpha(thisNusers+thisNmovies, 1);
	//Matrix Beta(thisNusers+thisNmovies, 1);
	Matrix Phi(thisNusers+thisNmovies, 1);
	Matrix temp;
	Matrix sim(thisNusers,thisNusers);
	

	L	=	inL;
	

	for(i=0;i<thisNusers;i++){
		for(j=0;j<thisNmovies;j++){
			P(i, thisNusers+j)	=	thistraining_set(i,j);
			P(thisNusers+j,i)	=	thistraining_set(i,j);
		}
		
	}
	temp	=	P.sum(1);
	
	
	temp1.fill(0);
	for(i=0;i<P.rows();i++){
		if(temp(i, 0)>0){
			temp1(i, i)=1/temp(i, 0);
		}
	}
	P	=	temp1*P;
	
	
	Phi.resize(Phi.rows(), L+1);
	
	switch(mode){
		case 1: //MODE_USERS:
			if(double(L/2)==floor(L/2)){ //pair=>user
				//cout << L << " impair" << endl;
				line	=	L;
			}
			else{//impair==>movies
				//cout << L << " pair" << endl;
				line	=	L-1;
			}
			break;
		case 2: //MODE_MOVIES:
			if(double(L/2)==floor(L/2)){ //pair=>user
				//cout << L << " impair" << endl;
				line	=	L-1;
			}
			else{//impair==>movies
				//cout << L << " pair" << endl;
				line	=	L;
			}
			break;
	}
	cout << "Computing ... " << endl;
	for(iu=0;iu<thisNusers;iu++){
		if(floor((double)iu/thisNusers*10)!=floor((double)(iu-1)/thisNusers*10)){
			cout << floor((double)iu/thisNusers*100) << "%...";
		}
		
		Phi.fill(0);
		Phi(iu, 0)	=	1;
		//Phi.resize(Phi.rows(), L+1);
		
		for(i=1;i<=L;i++){
			cvtemp	=	P.transpose()*Phi.column(i-1);
			for(j=0;j<Phi.rows();j++){
				Phi(j, i)	=	cvtemp(j);
			}
		}
		//cout << "Phi:" << Phi << endl;
		//cout << Phi.sum(2);
		
		ofstream fout("phi.dat");
		fout << Phi << endl;
		fout.close();
                
		switch(mode){
			case 1:
                            //USER-DIRECT
                            thissuggestions_val(iu,j)   =   0;
                            thissuggestions_val(iu, j)=thissuggestions_val(iu, j)+Phi(thisNusers+j, il);
                            /*
				for(j=0;j<thisNmovies;j++){
                                    for(il=0;il<L;il++){
					thissuggestions_val(iu, j)=thissuggestions_val(iu, j)+Phi(thisNusers+j, il);
                                    }
				}
                             **/
				break;
			case 2:
                            //MOVIE
                            //cout << "user_dwalk" << endl;
				temp.resize(thisNusers,1);
				for(i=0;i<thisNusers;i++){
					//temp(i,0)	=	Phi(i,line);
                                    sim(iu,i)   =   0;
                                    for(il=0;il<L;il++){
					sim(iu,i)	=	sim(iu,i)+Phi(i,il);
                                    }
				}
				/*
				temp	=	thistraining_set.transpose()*temp;
				for(j=0;j<thisNmovies;j++){
					//thissuggestions_val(iu, j)=temp(j,0);
					thissuggestions_val(iu, j)=temp(0,j);
					
				}
				 **/
				break;
		}
	}
        if(mode==2){
                UserBased(sim,k);
        }
	
	ofstream fout1("Suggestions_output.dat");
	fout1	<< thissuggestions_val << endl;
	fout1.close();
		
	
	return(0);
}

int recommend::SuggestMeSomethingDWalkUserBased(int k,int inL){

	int i, j, L, iu,il;
        
	Matrix P=Matrix(thisNusers+thisNmovies, thisNusers+thisNmovies);
	ColumnVector cvtemp;
	//double temp;
	Matrix temp1(thisNusers+thisNmovies, thisNusers+thisNmovies);
	//Matrix Alpha(thisNusers+thisNmovies, 1);
	//Matrix Beta(thisNusers+thisNmovies, 1);
	Matrix Phi(thisNusers+thisNmovies, 1);
	Matrix temp;
	Matrix sim(thisNusers,thisNusers);


	L	=	inL;


	for(i=0;i<thisNusers;i++){
		for(j=0;j<thisNmovies;j++){
			P(i, thisNusers+j)	=	thistraining_set(i,j);
			P(thisNusers+j,i)	=	thistraining_set(i,j);
		}

	}
	temp	=	P.sum(1);


	temp1.fill(0);
	for(i=0;i<P.rows();i++){
		if(temp(i, 0)>0){
			temp1(i, i)=1/temp(i, 0);
		}
	}
	P	=	temp1*P;


	Phi.resize(Phi.rows(), L+1);
	
	cout << "Computing ... " << endl;
	for(iu=0;iu<thisNusers;iu++){
		if(floor((double)iu/thisNusers*10)!=floor((double)(iu-1)/thisNusers*10)){
			cout << floor((double)iu/thisNusers*100) << "%...";
		}

		Phi.fill(0);
		Phi(iu, 0)	=	1;
		//Phi.resize(Phi.rows(), L+1);

		for(i=1;i<=L;i++){
			cvtemp	=	P.transpose()*Phi.column(i-1);
			for(j=0;j<Phi.rows();j++){
				Phi(j, i)	=	cvtemp(j);
			}
		}


                temp.resize(thisNusers,1);
		for(i=0;i<thisNusers;i++){
                    sim(iu,i)   =   0;
                    for(il=0;il<L;il++){
                        sim(iu,i)	=	sim(iu,i)+Phi(i,il);
                    }
                }

        }
        UserBased(sim,k);

	return(0);
}


int recommend::SuggestMeSomethingDWalkMovieBased(int k,int inL){

	int i, j, L, iu,il;

	Matrix P=Matrix(thisNusers+thisNmovies, thisNusers+thisNmovies);
	ColumnVector cvtemp;
	//double temp;
	Matrix temp1(thisNusers+thisNmovies, thisNusers+thisNmovies);
	//Matrix Alpha(thisNusers+thisNmovies, 1);
	//Matrix Beta(thisNusers+thisNmovies, 1);
	Matrix Phi(thisNusers+thisNmovies, 1);
	Matrix temp;
	Matrix sim(thisNusers,thisNusers);


	L	=	inL;


	for(i=0;i<thisNusers;i++){
		for(j=0;j<thisNmovies;j++){
			P(i, thisNusers+j)	=	thistraining_set(i,j);
			P(thisNusers+j,i)	=	thistraining_set(i,j);
		}

	}
	temp	=	P.sum(1);


	temp1.fill(0);
	for(i=0;i<P.rows();i++){
		if(temp(i, 0)>0){
			temp1(i, i)=1/temp(i, 0);
		}
	}
	P	=	temp1*P;


	Phi.resize(Phi.rows(), L+1);

	cout << "Computing ... " << endl;
	for(iu=0;iu<thisNusers;iu++){
		if(floor((double)iu/thisNusers*10)!=floor((double)(iu-1)/thisNusers*10)){
			cout << floor((double)iu/thisNusers*100) << "%...";
		}

		Phi.fill(0);
		Phi(iu, 0)	=	1;
		//Phi.resize(Phi.rows(), L+1);

		for(i=1;i<=L;i++){
			cvtemp	=	P.transpose()*Phi.column(i-1);
			for(j=0;j<Phi.rows();j++){
				Phi(j, i)	=	cvtemp(j);
			}
		}


                temp.resize(thisNusers,1);
		for(i=0;i<thisNmovies;i++){
                    sim(iu,i)   =   0;
                    for(il=0;il<L;il++){
                        sim(iu,i)	=	sim(iu,i)+Phi(thisNusers+i,il);
                    }
                }

        }
        MovieBased(sim,k);

	return(0);
}

int recommend::PutSuggestionsInFile(string outf){
    ofstream fout(outf.data());
    fout << thissuggestions_val << endl;
    return(0);
}